package calvin.study.springboot.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import calvin.study.springboot.dao.LogDao;
import calvin.study.springboot.dao.ProductDao;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;

@Service
public class ProductService {

	@Autowired
	private ProductDao productDao;
	@Autowired
	private LogDao logDao;
	@Autowired
	private ProductService productService;
	@Autowired
	private ProductLogService logService;

	// 1.方法均在同一类中，doHandlerProduct调用insertLog（），insertLog事物传播方式为创建新事物，当执行完两个方法后，发生异常。
	// 执行结果：两个方法均未插入数据，即使insertLog事物传播方式为创建新事物
	// 原因：同一个类中，属于同一个代理对象，此时配置传播方式等同于无效配置。
	// 解决方式：注入一个service，提供insertLog方法进行调用 或
	// AopContext.currentProxy()代理工具，返回代理类，再调用（启动类中配置该注解，@EnableAspectJAutoProxy(exposeProxy=true)
	// 才能生效）
	@Transactional(propagation = Propagation.REQUIRED)
	public void doHandlerProduct(String productName) {
		// ProductService productService = (ProductService) AopContext.currentProxy();

		insertProduct(productName);

		// productService.insertLog();
		productService.insertLog();
		// int i =1/0;
	}

	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public void updateProductNumber(int productId, int productNumber) {
		// 更新商品数量
		productDao.update(productId, productNumber);

		// 日志
		logDao.insert(6L, 2, "update商品数量：" + productNumber);

		System.out.println(1 / 0);
	}

	public void insertProduct(String productName) {
		productDao.insert(productName);
		System.out.println("新增商品成功。。。");
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void insertLog() {
		logDao.insert(111L, 2, "新增商品");
	}
	
	
	public void txPropagationA(int productId, int productNumber) {
		System.out.println(1 / 0);
		// 更新商品数量
		productDao.update(productId, productNumber);
		// 日志
		logService.insertLog01(RandomUtil.randomLong(0, 999), 2, "update商品数量：" + productNumber);
	}
	
	
	public void txPropagationB(int productId, int productNumber) {
		// 更新商品数量
		productDao.update(productId, productNumber);
		// 日志
		logService.insertLog02(RandomUtil.randomLong(0, 999), 2, "update商品数量：" + productNumber);
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void txPropagationC(int productId, int productNumber) {
		// 更新商品数量
		productDao.update(productId, productNumber);
		// 日志
		logService.insertLog02(RandomUtil.randomLong(0, 999), 2, "update商品数量：" + productNumber);
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void txPropagationD(int productId, int productNumber) {
		// 更新商品数量
		productDao.update(productId, productNumber);
		// 日志
		logService.insertLog04(RandomUtil.randomLong(0, 999), 2, "update商品数量：" + productNumber);
		System.out.println(1 / 0);
	}
	
	
	@Transactional
	public void nestedTest1() {
		//首先，外部事务默认为required，而内层事物（nested）由于外部具备事务，则融合到外部事务。
		//由于子事物抛出了异常，外部事物并没有捕获子事物异常，变成因子异常，导致外部事物也发生回滚。
		productService.insertProduct("小米"+RandomUtil.randomString(10));
		
		//捕获子事物异常，避免造成因子异常，导致父事物失效。
		try {
			logService.insertLog05(RandomUtil.randomLong(0, 999), 2, "子事物为嵌套事物");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Transactional(propagation=Propagation.NESTED)
	public void nestedTest2() {
		productService.insertProduct("小米"+RandomUtil.randomString(10));
		
		logService.insertLog06(RandomUtil.randomLong(0, 999), 2, "子事物为嵌套事物");
		throw new RuntimeException();
	}

}
